
#include "utilprod.h"
#include <stdio.h>
#include <tchar.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "../LIBTR/LIBTR.h"
#include "utilcvt.h"

#ifdef _OEM_FORCE_LICENSE_READ_
#pragma message("OEM Force Read Mode")
#else
#pragma message("Not using OEM Force Read Mode")
#endif

#ifdef TR_TREXE_ENABLE

int TR_RegionBegin_9();
int TR_RegionEnd_9();
// the following variable is set equal by the release scrambling software.
DWORD	TR_ScrambleEnable_9	= TR_SCRAMBLE_ENABLE_9^1;
DWORD	TR_RelocateSize_9	= 3000;
WORD	TR_RelocateTable_9[3000] = {0xffff};

DWORD	TR_ChecksumEnable_9	= TR_CHECKSUM_ENABLE_9^1;
DWORD	TR_ChecksumValue_9 = TR_CHECKSUM_VALUE_9;

// the following function is used for descrambling.
void __fastcall TR_Descramble_9()
	{
	if (TR_ScrambleEnable_9 == TR_SCRAMBLE_ENABLE_9)
		{
		TR_ScrambleEnable_9 ^= 1;	// ensure we don't do this twice!
		CTR::Descramble((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_SCRAMBLE_SEED_9, TR_RelocateTable_9);
		//DP("Scramble_9 validated with %x\n",TR_SCRAMBLE_SEED_9);
		}
	}


#endif //TR_TREXE_ENABLE

/////////////////////////////////////////////////////////////////////////////
// CUTILprod

CUTILprod::CUTILprod(HKEY hkey, char *subkey)
	{
#ifdef TR_TREXE_ENABLE
	TR_Descramble_9();
#endif
	this->hkey = 0;

	TR_VERIFY_SOFTICE(TR_DEFAULT_ACTION);
	TR_VERIFY_ATTACHED_DEBUGGER(TR_DEFAULT_ACTION);
	TR_VERIFY_VTUNE_30(TR_DEFAULT_ACTION);

	m_pDatePfx = "D";
	m_pRegionPfx = "R";
	m_pLicPfx = "P";

	m_bUpgradable = true;
	m_bUseNewLicense = false;
	m_bRecoverOEM = false;
	m_TrialExpireDays = UTIL_PROD_EXPIRE_VAR;

	m_hkRoot = hkey;
	strcpy(m_SubKey, subkey ? subkey : "");

	if(RegOpenKeyExA(hkey,subkey,0,KEY_READ|KEY_WRITE,&this->hkey)!=ERROR_SUCCESS)
		{
		if(RegCreateKeyExA(hkey,subkey,0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_READ|KEY_WRITE,NULL,&this->hkey,NULL)!=ERROR_SUCCESS)
			if(RegOpenKeyExA(hkey,subkey,0,KEY_READ,&this->hkey)!=ERROR_SUCCESS)
				return;
		}
	}

#ifdef TR_TREXE_ENABLE
// the following function is used as place marker.
int TR_RegionBegin_9() 
	{	// needs to have some meat otherwise it compresses with below.
	static int play;
	for (int i=0;i<100;i++) play+=play;
	return play;
	}
#endif //TR_TREXE_ENABLE


UINT64 CUTILprod2App::GetCommonOpts()
	{
	UINT64 opts = 0;
	CUTILprodinfo2 inf1;
	CUTILprodGeneric2 prod1(m_hkRoot, m_SubKey);
	if(prod1.GetInfo(&inf1, FALSE) == 0)
		opts = inf1.option & ~LIC_OPTION_COMMON_MASK; // remove the trial bits
	return opts;
	}

int CUTILprod::GenerateKey(CUTILprodinfo *ip, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	unsigned short sum;

	memcpy(buf,&ip->product,2);
	memcpy(buf+2,&ip->version,2);
	memcpy(buf+4,&ip->customer,2);
	memcpy(buf+6,&ip->option,4);
	sum = CheckSum(buf,10);
	memcpy(buf+10,&sum,2);

	Scramble(uid,ip->key,buf,12);
	return 0;
	}

int CUTILprod::GenerateKey(CUTILprodinfo2 *ip, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	unsigned long sum;
	int ret = 0;

	if (m_bUseNewLicense)
	{
		memcpy(buf,&ip->product,2);
		memcpy(buf+2,&ip->version,2);
		memcpy(buf+4,&ip->customer,2);
		memcpy(buf+6,&ip->option,8);
		sum = CheckSum2(buf,14);	
		CUTILcvt::LittleEndian((unsigned long *)(buf+14), &sum);
//	memcpy(buf+14,&sum,4);
		Scramble(uid,ip->key,buf,18);
	} else {
		CUTILprodinfo info1;
		info1.customer = ip->customer;
		info1.option = (DWORD)(ip->option & 0xffffffff);
		info1.product = ip->product;
		info1.version = ip->version;
		ret = GenerateKey(&info1,uid);
		memcpy(ip->key,info1.key,UTIL_PROD_LEN);
		return ret;		
	}

	return 0;
	}

int CUTILprod::GenerateInfo(CUTILprodinfo *ip, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	unsigned short sum;

	if(Descramble(uid,ip->key,buf,sizeof(buf))<0)
		return -1;
	memcpy(&sum,buf+10,2);
	if(sum!=CheckSum(buf,10)) 
		return -1;
	memcpy(&ip->product,buf,2);
	memcpy(&ip->version,buf+2,2);
	memcpy(&ip->customer,buf+4,2);
	memcpy(&ip->option,buf+6,4);
	return 0;
	}

int CUTILprod::GenerateInfo(CUTILprodinfo2 *ip, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	unsigned long sum,sum2,tmp;

	if(Descramble(uid,ip->key,buf,sizeof(buf))<0)
		return -1;
	//memcpy(&sum,buf+14,4);
	CUTILcvt::LittleEndian(&sum, buf+14);
	tmp=CheckSum2(buf,14);
	CUTILcvt::LittleEndian(&sum2, &tmp);
	if(sum!=sum2)
		return -1;
	memcpy(&ip->product,buf,2);
	memcpy(&ip->version,buf+2,2);
	memcpy(&ip->customer,buf+4,2);
	memcpy(&ip->option,buf+6,8);
	return 0;
	}

int CUTILprod2::GenerateInfo(CUTILprodinfo *ip, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	unsigned long sum,sum2,tmp;

	if(Descramble(uid,ip->key,buf,sizeof(buf))<0)
		return -1;
	//memcpy(&sum,buf+14,4);
	CUTILcvt::LittleEndian(&sum, buf+14);
	tmp=CheckSum2(buf,14);
	CUTILcvt::LittleEndian(&sum2, &tmp);
	if(sum!=sum2)
		return -1;
	memcpy(&ip->product,buf,2);
	memcpy(&ip->version,buf+2,2);
	memcpy(&ip->customer,buf+4,2);
	memcpy(&ip->option,buf+6,4);
	return 0;
	}

int CUTILprod::InfoToText(CUTILprodinfo *ip, char *buf, unsigned long len)
	{
	unsigned long uid = GetUID();
	unsigned long offset = (sizeof(unsigned long)*8+4)/5;

	GenerateKey(ip,uid);
	if(len<=offset+strlen(ip->key))
		return -1;

	Scramble(UTIL_PROD_UID,buf,&uid,sizeof(unsigned long));
	strcat(buf,ip->key);
	return 0;
	}

int CUTILprod::InfoToText(CUTILprodinfo2 *ip, char *buf, unsigned long len)
	{
	unsigned long uid = GetUID();
	unsigned long offset = (sizeof(unsigned long)*8+4)/5;

	CUTILprod::GenerateKey(ip,uid);
	if(len<=offset+strlen(ip->key))
		return -1;
	Scramble(UTIL_PROD_UID,buf,&uid,sizeof(unsigned long));
	strcat(buf,ip->key);
	return 0;
	}

int CUTILprod::TextToInfo(CUTILprodinfo *ip, unsigned long *uidp, char *buf)
	{
	unsigned long offset = (sizeof(unsigned long)*8+4)/5;

	strncpy(ip->key,buf,offset);
	ip->key[offset] = '\0';
	if(Descramble(UTIL_PROD_UID,ip->key,uidp,sizeof(unsigned long))<0)
		return -1;
	strcpy(ip->key,buf+offset);
	return GenerateInfo(ip,*uidp);
	}


int CUTILprod::TextToInfo(CUTILprodinfo2 *ip, unsigned long *uidp, char *buf)
	{
	unsigned long offset = (sizeof(unsigned long)*8+4)/5;

	strncpy(ip->key,buf,offset);
	ip->key[offset] = '\0';
	if(Descramble(UTIL_PROD_UID,ip->key,uidp,sizeof(unsigned long))<0)
		return -1;
	strcpy(ip->key,buf+offset);
	return GenerateInfo(ip,*uidp);
	}

unsigned short CUTILprod::ConvertVersion(char *verstr, unsigned char build)
	{
	unsigned short version = 0;
	int i = 0;

	for(; *verstr; verstr++)
		{
		if(*verstr>='0' && *verstr<='9')
			{
			version = version*10+*verstr-'0';
			if(++i==4)
				break;
			}
		}

	for(; i<4; i++)
		version *= 10;

	if (build>99)
		build=99;

	version+=build;

	return version;
	}

CUTILprod::~CUTILprod()
	{
	if(hkey)
		RegCloseKey(hkey);
	}



int CUTILprod::GetInfo(CUTILprodinfo *ip, BOOL bRecover)
	{
	char name[UTIL_PROD_LEN];
	char name2[UTIL_PROD_LEN];
	unsigned long uid, uid2;
	int ret;
#ifdef _XENON
	ip->option = 0xffffffff;
	return 0;
#endif

	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pLicPfx);
	sprintf(name2,"%s%s_1",UTIL_PROD_NAME, m_pLicPfx);
	if(hkey==0)
		return -1;
	uid = GetUID();
	uid2 = GetTimeUid(false, true);

	if(GetRegistry(name,ip->key,sizeof(ip->key),uid)<0)
		return -1;
	ret = GenerateInfo(ip,uid);
	if(ret==0)// || m_bUpgradable==false)
	{
		SetBackupInfo(ip, kBackupNic);
		return ret;
	}

	// try backup using date key
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}

	// try backup using nic key
	uid2 = GetNicUid();
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}

	// try backup using disk key
	uid2 = GetDiskUid();
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}

	// this covers legacy keys
	uid = GetDiskUid();	// try with disk sn
	if(GetRegistry(name,ip->key,sizeof(ip->key),uid)<0)
		return -1;
	ret = GenerateInfo(ip,uid);
	if(ret==0)
		goto LDone;

	if(m_bUpgradable)
	{ // upgrade from old license
		uid = GetTimeUid(true, true);	// try with std time
		if(GetRegistry(name,ip->key,sizeof(ip->key),uid)<0)
			return -1;
		ret = GenerateInfo(ip,uid);
		if(ret==0)
			goto LDone;

		uid = GetTimeUid(true, false);	// try with daylight time
		if(GetRegistry(name,ip->key,sizeof(ip->key),uid) < 0)
			return -1;
		ret = GenerateInfo(ip,uid);
		if(ret==0)
			goto LDone;
	}

	uid = GetTimeUid(false, true);	// try with std time on System dir
	if(GetRegistry(name,ip->key,sizeof(ip->key),uid)<0)
		return -1;
	ret = GenerateInfo(ip,uid);
	if(ret==0)
		goto LDone;

	uid = GetTimeUid(false, false);	// try with daylight time on System dir
	if(GetRegistry(name,ip->key,sizeof(ip->key),uid) < 0)
		return -1;
	ret = GenerateInfo(ip,uid);
	if(ret==0)
		goto LDone;

LDone:
	if(ret!=0)
		return ret;
	if (bRecover==TRUE)
		SetInfo(ip); // convert the key to new key format
	return ret;
	}

int CUTILprod::GetInfo(CUTILprodinfo2 *ip2, BOOL bRecover)
{
	CUTILprodinfo ip1;
	int ret = GetInfo(&ip1, bRecover);
	ip2->customer = ip1.customer;
	memcpy(ip2->key,ip1.key,UTIL_PROD_LEN);
	ip2->option = ((unsigned __int64)ip1.option) & 0x00000000ffffffff;
	ip2->product = ip1.product;
	ip2->version = ip1.version;
	return ret;
}

int CUTILprod2::GetInfo(CUTILprodinfo2 *ip, BOOL bRecover)
	{
	char name[UTIL_PROD_LEN];
	char name2[UTIL_PROD_LEN];
	char name3[UTIL_PROD_LEN];
	unsigned long uid, uid2;
	int ret;

	if(hkey==0)
		return -1;

	sprintf(name, "%s%s",  UTIL_PROD_NAME, m_pLicPfx);
	sprintf(name2,"%s%s_1",UTIL_PROD_NAME, m_pLicPfx);
	sprintf(name3,"%s%s_2",UTIL_PROD_NAME, m_pLicPfx);

	uid  = GetUID();
	if(GetRegistry(name,ip->key,sizeof(ip->key),uid)<0)
		return -1;
	ret = CUTILprod::GenerateInfo(ip,uid);
	if(ret==0)// || m_bUpgradable==false)
	{
		CUTILprod::SetBackupInfo(ip, kBackupNic);
		return ret;
	}

	// try backup using new nic key
	uid2 = GetNicUid2();
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = CUTILprod::GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}


	// try using old time stamp as primary
	uid2 = GetTimeUid2(true);
	if(GetRegistry(name,ip->key,sizeof(ip->key),uid2)==0)
	{
		ret = CUTILprod::GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
				goto LDone;
	}

	// try backup using old nic key
	uid2 = GetNicUid();
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = CUTILprod::GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}

	// try backup using disk SN key
	uid2 = GetDiskUid();
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = CUTILprod::GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}

	// try backup using old date key
	uid2 = GetTimeUid(false, true);
	if(GetRegistry(name2,ip->key,sizeof(ip->key),uid2)>=0)
	{
		ret = CUTILprod::GenerateInfo(ip,uid2);
		if(ret==0)// || m_bUpgradable==false)
			goto LDone;
	}

LDone:
	if(ret!=0)
		{
#ifdef _OEM_EMBEDDED_LIC_
		uid2 = GetProductUid();
		if(GetRegistry(name3,ip->key,sizeof(ip->key),uid2)>=0)
			{
			ret = CUTILprod::GenerateInfo(ip,uid2);
			}
		if(ret!=0)
			return ret;
#else
		return ret;
#endif
		}

	if (bRecover==TRUE)
		SetInfo(ip); // convert the key to new key format
	return ret;
	}

int CUTILprod2::GetInfo(CUTILprodinfo *ip1, BOOL bRecover)
	{
	CUTILprodinfo2 ip2;

	int ret = GetInfo(&ip2, bRecover);
	ip1->customer = ip2.customer;
	memcpy(ip1->key,ip2.key,UTIL_PROD_LEN);
	ip1->option = ((DWORD)ip2.option) & 0x00000000ffffffff;
	ip1->product = ip2.product;
	ip1->version = ip2.version;

	return ret;
	}

int CUTILprod::OpenOemLicense(CUTILprodinfo *ip, DWORD oemId)
	{
	int ret = -1;
#if defined(_DEBUG) || defined(_OEM_FORCE_LICENSE_READ_)
	char name[UTIL_PROD_LEN];
	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pLicPfx);
	bool bRead = AllowOemRead();
	if(bRead)
		{
		WORD firstKey = -1;
		for(int i = 0; i < 65535; i++)
			{
			if(GetRegistry(name,ip->key,sizeof(ip->key),i)<0)
				break;

			ret = CUTILprod::GenerateInfo(ip,i);
			if(ret == 0)
				{
				if(ip->customer == oemId)
					{
					ret = 0;
					break;
					}
				else if(firstKey == 0xffff)
					firstKey = ip->customer;
				else if(firstKey == ip->customer)
					{
					ret = -1;
					break; // once we cycle through the checksums, quit.
					}
				}
			}
		}
#endif
	if(ret < 0)
		{// clear out the license info
			memset(ip, 0, sizeof(*ip));
		}
	return ret;
	}

int CUTILprod2::OpenOemLicense(CUTILprodinfo2 *ip, DWORD oemId)
	{
	int ret = -1;
#if defined(_DEBUG) || defined(_OEM_FORCE_LICENSE_READ_)
	char name[UTIL_PROD_LEN];
	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pLicPfx);
	bool bRead = AllowOemRead();
	if(bRead)
		{
		WORD firstKey = -1;
		for(int i = 0; i < 65535; i++)
			{
			if(GetRegistry(name,ip->key,sizeof(ip->key),i)<0)
				break;

			ret = CUTILprod::GenerateInfo(ip,i);
			if(ret == 0)
				{
				if(ip->customer == oemId)
					{
					ret = 0;
					break;
					}
				else if(firstKey == 0xffff)
					firstKey = ip->customer;
				else if(firstKey == ip->customer)
					{
					ret = -1;
					break; // once we cycle through the checksums, quit.
					}
				}
			}
		}
#endif
	if(ret < 0)
		{// clear out the license info
			memset(ip, 0, sizeof(*ip));
		}

	return ret;
	}



int CUTILprod::SetBackupInfo(CUTILprodinfo *ip, EBackupType type)
	{
//		return 0;  //for test purpose. 

	unsigned long uid;
	switch(type)
	{
	case kBackupNic:
		uid = GetNicUid();
	break;
	case kBackupDirAttr:
		uid = GetTimeUid(false, true);
	break;
	case kBackupProductid:
		uid = GetProductUid();
	break;
	default:
		uid = GetDiskUid();
	break;	
	}
	return SetBackupInfo(ip, uid, type);
	}

int CUTILprod::SetBackupInfo(CUTILprodinfo2 *ip, EBackupType type)
	{
	unsigned long uid;
	switch(type)
	{
	case kBackupNic:
		uid = GetNicUid2();
	break;
	case kBackupDirAttr:
		uid = GetTimeUid3();
	break;
	case kBackupProductid:
		uid = GetProductUid();
	break;
	default:
		uid = GetDiskUid();
	break;	
	}
	return SetBackupInfo(ip, uid, type);
	}


int CUTILprod::SetBackupInfo(CUTILprodinfo *ip, DWORD uid, EBackupType type)
	{
	if(m_bUseNewLicense) // to protect overwriting of new license key.
		return 0;
	char name[UTIL_PROD_LEN];
	CUTILprodinfo ip2;
		sprintf(name,"%s%s_1",UTIL_PROD_NAME, m_pLicPfx);
	memcpy(&ip2, ip, sizeof(ip2));
	if(GenerateKey(&ip2, uid)>= 0)
		return SetRegistry(name, ip2.key, strlen(ip2.key), uid);
	return -1;
	}

int CUTILprod::SetBackupInfo(CUTILprodinfo2 *ip, DWORD uid, EBackupType type)
	{
	char name[UTIL_PROD_LEN];
	CUTILprodinfo2 ip2;
		sprintf(name,"%s%s_1",UTIL_PROD_NAME, m_pLicPfx);
	memcpy(&ip2, ip, sizeof(ip2));
	if(GenerateKey(&ip2, uid)>= 0)
		return SetRegistry(name, ip2.key, strlen(ip2.key), uid);
	return -1;
	}

int CUTILprod::SetProductidInfo(CUTILprodinfo2 *ip2)
	{
#ifdef _OEM_EMBEDDED_LIC_ 
	if(m_bRecoverOEM)
		{
		if(ip2==0)
			return -1;
		char name[UTIL_PROD_LEN];
		sprintf(name,"%s%s_2",UTIL_PROD_NAME, m_pLicPfx);
		int uid = GetProductUid();
		if(GenerateKey(ip2, uid)>= 0)
			return SetRegistry(name, ip2->key, strlen(ip2->key), uid);
		}
#endif
	return -1;
	}


int CUTILprod::SetInfo(CUTILprodinfo *ip)
	{
	unsigned long uid = GetUID();
	return SetInfo(ip, uid);
	}


int CUTILprod::SetInfo(CUTILprodinfo *ip, DWORD uid)
	{
	char name[UTIL_PROD_LEN];
	if(m_bUseNewLicense) // to protect overwriting of new license key.
		return 0;
//	SetBackupInfo(ip);
	SetBackupInfo(ip, kBackupNic);
	if(hkey==0 || GenerateKey(ip,uid)<0)
		return -1;
	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pLicPfx);

	return SetRegistry(name,ip->key,strlen(ip->key),uid);
	}

int CUTILprod2::SetInfo(CUTILprodinfo2 *ip)
	{
	unsigned long uid = GetUID();
	return SetInfo(ip, uid);
	}

int CUTILprod2::SetInfo(CUTILprodinfo2 *ip, DWORD uid)
	{
	char name[UTIL_PROD_LEN];

	SetBackupInfo(ip, kBackupNic);
	SetProductidInfo(ip);
	if(hkey==0 || GenerateKey(ip,uid)<0)
		return -1;
	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pLicPfx);

	return SetRegistry(name,ip->key,strlen(ip->key),uid);
	}

int CUTILprod::SetKey(char *key)
	{
	char name[UTIL_PROD_LEN];
	unsigned long uid = GetUID();

	if(hkey==0)
		return -1;
	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pLicPfx);
	return SetRegistry(name,key,strlen(key),uid);
	}

int CUTILprod::GetRegistry(char *name, void *value, unsigned long len, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	DWORD buflen = sizeof(buf);

	if(RegQueryValueExA(hkey,name,0,NULL,(LPBYTE)buf,&buflen)!=ERROR_SUCCESS)
		return -1;
	return Descramble(uid,buf,value,len);
	}

int CUTILprod::SetRegistry(char *name, void *value, unsigned long len, unsigned long uid)
	{
	char buf[UTIL_PROD_LEN];
	char buf2[UTIL_PROD_LEN];
	DWORD buflen = sizeof(buf2);

	static long synchFlag = 0;

	Scramble(uid,buf,value,len);

	// use spin lock for thread saftey
	while(InterlockedExchange(&synchFlag, 1))
		Sleep(0);

	if(RegQueryValueExA(hkey,name,0,NULL,(LPBYTE)buf2,&buflen)==ERROR_SUCCESS)
	{
		if(strncmp(buf,buf2,UTIL_PROD_LEN)==0)
		{
			InterlockedExchange(&synchFlag, 0);
			return 0;
		}
	}

	int ret = RegSetValueExA(hkey,name,0,REG_SZ,(LPBYTE)buf,strlen(buf)+1);
	InterlockedExchange(&synchFlag, 0);
	return ret == ERROR_SUCCESS ? 0 : -1;
	}


void CUTILprod::SetTrialStartDate(time_t startDate)
{
	char TrailStartName[UTIL_PROD_LEN];
	char AppExpireName[UTIL_PROD_LEN];

	sprintf(TrailStartName,"%s%s",UTIL_PROD_NAME, m_pDatePfx);
	sprintf(AppExpireName,"%s%s%s",UTIL_PROD_NAME, m_pDatePfx, UTIL_APPEXPIRE_NAME);

	unsigned long uid = GetUID();

	time_t t1 = 0;
	SetRegistry(TrailStartName,&startDate,sizeof(startDate),uid);
	if (GetRegistry(AppExpireName,&t1,sizeof(t1),uid) < 0 || AppExpireName==0)
		SetRegistry(AppExpireName,&startDate,sizeof(startDate),uid);
}

time_t CUTILprod::GetTrialStartDate()
{
	char name[UTIL_PROD_LEN];
	unsigned long uid = GetUID();
	time_t t1 = 0;
	sprintf(name,"%s%s",UTIL_PROD_NAME, m_pDatePfx);

	if(GetRegistry(name,&t1,sizeof(t1),uid) < 0)
	{
		t1 = time(NULL);
		SetTrialStartDate(t1);
	}

	return t1;
}

long CUTILprod::GetTrialTimeLeft()
{
	time_t curTime;
	time_t trialStart;
	long deltaTimeDays;

	time(&curTime); // get current time
	trialStart = GetTrialStartDate(); // get trial start time
	
	deltaTimeDays = (curTime - trialStart)/(3600*24);

	if(IsExpired())
		return 0; //time's up

	return deltaTimeDays > 0 ? m_TrialExpireDays-deltaTimeDays : m_TrialExpireDays;
}

BOOL CUTILprod::IsExpired()
{
#ifdef _TIMEBOMB

	// this code will prevent use of file after the expire time elapses

	SYSTEMTIME	stExpire = {_TIMEBOMB_YEAR, _TIMEBOMB_MONTH, 0, _TIMEBOMB_DAY, 0, 0, 0, 0};
	FILETIME ftExpire, ftCur;
	SystemTimeToFileTime(&stExpire, &ftExpire);
	GetSystemTimeAsFileTime(&ftCur);
	if(CompareFileTime(&ftExpire, &ftCur) <= 0)
		return true;

#endif
	char TrailStartName[UTIL_PROD_LEN];
	char AppExpireName[UTIL_PROD_LEN];
	unsigned long uid = GetUID();
	time_t t1 = 0; // TrailStartDate
	time_t t2;
	time_t t3 = 0; // AppExpTemp

	time(&t2);

	bool bRet = false;
	sprintf(TrailStartName,"%s%s",UTIL_PROD_NAME, m_pDatePfx);
	sprintf(AppExpireName,"%s%s%s",UTIL_PROD_NAME, m_pDatePfx, UTIL_APPEXPIRE_NAME);
	
	t1 = GetTrialStartDate();

	if ((t2-t1)<0 || (t2-t1>m_TrialExpireDays*86400))
	{
		bRet = true;
		SetRegistry(AppExpireName,&t3,sizeof(t3),uid);
	}

	if(GetRegistry(AppExpireName,&t3,sizeof(t3),uid)<0 || t3==0 || t3!=t1)
		bRet = true;

	return bRet;
}


unsigned long CUTILprod::GetTimeUid(bool bUseDefSystemDir, bool bReferenceToStdTime)
{
	TCHAR *subkey, *name;
	HKEY h;
	unsigned long uid;
	DWORD len = sizeof(uid);

	subkey = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
	name = TEXT("InstallDate");

	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey,0,KEY_READ,&h)==ERROR_SUCCESS)
		{
		if(RegQueryValueEx(h,name,0,NULL,(LPBYTE)&uid,&len)!=ERROR_SUCCESS)
			uid = UTIL_PROD_UID;
		RegCloseKey(h);
		}
	else
		uid = UTIL_PROD_UID;

	char		DirName[MAX_PATH];
	GetSystemDirectoryA(DirName, MAX_PATH);
	if(!bUseDefSystemDir)
		{ // replace "System32" with System"
		char* p = strstr(DirName, "System32");
		if(p)
			*(p + 6) = '\0';
		}

	struct _stat fstat;
	_stat(DirName, &fstat);
	time_t idtime = fstat.st_ctime;

	TIME_ZONE_INFORMATION tz;
	DWORD tzi = GetTimeZoneInformation(&tz);

	if(bReferenceToStdTime && tzi == TIME_ZONE_ID_DAYLIGHT)
		idtime -= 3600;
	else if( !bReferenceToStdTime && tzi == TIME_ZONE_ID_STANDARD)
		idtime += 3600;

/*	if(!bUseDefSystemDir)
		{
		DWORD dw1;
		dw1 = (LOWORD(idtime) ^ 0x0419) << 16;
		dw1 |= (HIWORD(idtime) ^ 0x1960);

		uid = dw1;
		}
	else
*/		{
		uid ^= idtime;
		}

	return uid;
	}


unsigned long CUTILprod::GetTimeUid2(bool bUseDefSystemDir)
	{
#ifdef _XENON
	return UTIL_PROD_UID;
#else
	TCHAR *subkey, *name;
	HKEY h;
	unsigned long uid;
	DWORD len = sizeof(uid);

	subkey = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
	name = TEXT("InstallDate");

	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey,0,KEY_READ,&h)==ERROR_SUCCESS)
		{
		if(RegQueryValueEx(h,name,0,NULL,(LPBYTE)&uid,&len)!=ERROR_SUCCESS)
			uid = UTIL_PROD_UID;
		RegCloseKey(h);
		}
	else
		uid = UTIL_PROD_UID;

	char		DirName[MAX_PATH];
	if(!GetSystemDirectoryA(DirName, MAX_PATH))
		return 0;
	if(!bUseDefSystemDir)
		{ // replace "System32" with System"
		char* p = strstr(DirName, "System32");
		if(p)
			*(p + 6) = '\0';
		}
	HANDLE hFile; 
	hFile = CreateFileA(DirName,				// open MYFILE.TXT 
		            GENERIC_READ,           // open for reading 
			        FILE_SHARE_READ,        // share for reading 
				    NULL,                   // no security 
					OPEN_EXISTING,          // existing file only 
					FILE_FLAG_BACKUP_SEMANTICS,  // for directory 
					NULL);                  // no attr. template 
 
	if (hFile == INVALID_HANDLE_VALUE) 
        return 0;
	FILETIME CreationTime;
	if(!GetFileTime( hFile, &CreationTime, 0, 0))
	{
		CloseHandle(hFile);
		return 0;
	}
	CloseHandle(hFile);

	uid ^= CreationTime.dwLowDateTime^CreationTime.dwHighDateTime;

	return uid;
#endif // !XENON
	}

unsigned long CUTILprod::GetTimeUid3()
	{

	TCHAR *subkey, *name;
	HKEY h;
	unsigned long uid;
	DWORD len = sizeof(uid);

	subkey = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
	name = TEXT("InstallDate");

	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey,0,KEY_READ,&h)==ERROR_SUCCESS)
		{
		if(RegQueryValueEx(h,name,0,NULL,(LPBYTE)&uid,&len)!=ERROR_SUCCESS)
			uid = UTIL_PROD_UID;
		RegCloseKey(h);
		}
	else
		uid = UTIL_PROD_UID;

	uid ^= (uid >> 4)^(uid << 4);
	uid |= uid;

	return uid;
	}

unsigned long CUTILprod::GetProductUid()
	{

	TCHAR *subkey, *subkey2, *name;
	HKEY h;
	unsigned long uid;
	DWORD len = sizeof(uid);
	name = TEXT("ProductId");
	TCHAR  productid[1024];

	subkey   = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion");
	subkey2  = TEXT("Software\\Microsoft\\Windows\\CurrentVersion");

	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey,0,KEY_READ,&h)==ERROR_SUCCESS)
		{
		if(RegQueryValueEx(h,name,0,NULL,(LPBYTE)productid,&len)!=ERROR_SUCCESS)
			uid = UTIL_PROD_UID;
		RegCloseKey(h);
		}
	else if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey2,0,KEY_READ,&h)==ERROR_SUCCESS)
		{
		if(RegQueryValueEx(h,name,0,NULL,(LPBYTE)productid,&len)!=ERROR_SUCCESS)
			uid = UTIL_PROD_UID;
		RegCloseKey(h);
		}
	else
		return UTIL_PROD_UID;

	uid = productid[1]<<24 | productid[2]<<16 | productid[3]<<8 | productid[4];
	for(unsigned int i=0;i<len;i++)
		{
		uid |= uid << 1 | productid[i] | productid[i] << 20;
		}
	uid ^= (uid >> 1)^(uid << 2);

	return uid;
	}

unsigned long CUTILprod::GetNicUid()
{
#if !defined(_XENON)
	RPC_STATUS res = E_FAIL;
	UUID uid;

	typedef RPC_STATUS (RPC_ENTRY *PFNType)(UUID* Uuid);
	PFNType pfn = NULL;

	HINSTANCE hLib = LoadLibrary(TEXT("rpcrt4.dll"));
	DWORD err = 0;

	if( hLib)
	{

		pfn = (PFNType)GetProcAddress(hLib, "UuidCreateSequential");
		if(!pfn)
			pfn = (PFNType)GetProcAddress(hLib, ("UuidCreate"));

	
		if(pfn)
			res = pfn(&uid);
		FreeLibrary(hLib);
	}


	if(res == RPC_S_OK)
	{
		DWORD ret;
		DWORD dw1 = *((DWORD*)&uid.Data4[4]);
		if (dw1 != 0)
		{
			ret = (LOWORD(dw1) ^ 0x0419) << 16;
			ret |= (HIWORD(dw1) ^ 0x1960);
			return ret;
		}
	} 
#endif
	return GetTimeUid(false, true);
}

const DWORD CUTILprod::kDefNicUid =  0x68CA29A5;

unsigned long CUTILprod::GetNicUid2()
{
#if !defined(_XENON)
	RPC_STATUS res = E_FAIL;
	UUID uid;

	typedef RPC_STATUS (RPC_ENTRY *PFNType)(UUID* Uuid);
	PFNType pfn = NULL;

	HINSTANCE hLib = LoadLibrary(TEXT("rpcrt4.dll"));
	DWORD err = 0;

	if( hLib)
	{

		pfn = (PFNType)GetProcAddress(hLib, ("UuidCreateSequential"));
		if(!pfn)
			pfn = (PFNType)GetProcAddress(hLib, ("UuidCreate"));

	
		if(pfn)
			res = pfn(&uid);
		FreeLibrary(hLib);
	}


	if(res == RPC_S_OK)
	{
		DWORD ret;
		DWORD dw1 = *((DWORD*)&uid.Data4[4]);
		if (dw1 != 0)
		{
			ret = (LOWORD(dw1) ^ 0x0419) << 16;
			ret |= (HIWORD(dw1) ^ 0x1960);
			return ret;
		}
	} 
#endif
	return kDefNicUid;
}

unsigned long CUTILprod::GetDiskUid()
{
#if !defined(_XENON)
	TCHAR buf1[32], buf2[MAX_PATH];
	DWORD dw1, dw2, dwSn;

	SetErrorMode(SEM_FAILCRITICALERRORS);

	BOOL bSuccess = GetVolumeInformation(	_T("C:\\"), 
											buf1, 
											sizeof(buf1)/sizeof(TCHAR), 
											&dwSn, 
											&dw1, 
											&dw2, 
											buf2,
											MAX_PATH);

	if(bSuccess)
	{
		dw1 = (LOWORD(dwSn) ^ 0x0419) << 16;
		dw1 |= (HIWORD(dwSn) ^ 0x1960);
		return dw1;
	}

	bSuccess = GetVolumeInformation(	_T("D:\\"), 
											buf1, 
											sizeof(buf1)/sizeof(TCHAR), 
											&dwSn, 
											&dw1, 
											&dw2, 
											buf2,
											MAX_PATH);

	if(bSuccess)
	{
		dw1 = (LOWORD(dwSn) ^ 0x0419) << 16;
		dw1 |= (HIWORD(dwSn) ^ 0x1960);
		return dw1;
	}
#endif
	return GetTimeUid(true, true);

}

unsigned long CUTILprod::GetUID()
	{
	return GetTimeUid(true, true);
	}
unsigned long CUTILprod::GetUID2()
	{
	return GetDiskUid(); //;GetTimeUid(true, true);
	}
unsigned long CUTILprod2::GetUID2()
	{
	return GetTimeUid3();
	}

unsigned short CUTILprod::CheckSum(char *buf, unsigned long len)
	{
	unsigned short sum = 0;
	unsigned long i;

	for(i=0; i<len; i++)
		sum ^= (unsigned char)buf[i]<<((i&0x01)*8);
	return sum;
	}

DWORD CUTILprod::CheckSum2(char *buf, unsigned long len)
	{
	unsigned short sum1 = 23870, sum2=0x5a5a;
	short m1=23893;
	unsigned i;

	for(i=0; i<len; i++)
	{
		sum1 += m1*buf[i]*(i+1);
		sum2 += buf[i]^sum1;
	}
	return sum1 | ((long) sum2<<16);

	}

DWORD RotateDwBits(DWORD& dw, int amt)
{
	amt %=32;

	dw = dw>>amt | dw<<(32-amt);
	return dw;
}

// old license characters
const char kOldLicKeyChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 
							 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 
							 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'};

// New license characters
char kNewLicKeyChars[sizeof(kOldLicKeyChars)] = {'\0'};
/*
{	'2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 
	'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 
	'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z', 'V'
};
*/

// we want to init this static buffer without using literals, since the literals would
// appear in contiguous area of the static data space.

void InitKeyChars()
{
	static long bLock = 0;

	while(InterlockedExchange(&bLock, 1))
		Sleep(0);

	if(kNewLicKeyChars[0] == '\0')
	{ // need to init
		memset(kNewLicKeyChars, 0, sizeof(kNewLicKeyChars));

		int i = 0;

		// handle the digits
		for(; i < 8; i++)
			kNewLicKeyChars[i] = '0' + i+2;

		// handle letters upto h
		for(;i < 16; i++)
			kNewLicKeyChars[i] = 'A' + i -8;

		// handle letters to O
		for(; i < 21; i++)
			kNewLicKeyChars[i] = 'A' + i+1-8;

		// letters to U
		for(; i < 27; i++)
			kNewLicKeyChars[i] = 'A' + i+2-8;

		// W-Z
		for(; i < 31; i++)
			kNewLicKeyChars[i] = 'A' + i+3-8;

		// finally V
		kNewLicKeyChars[i] = 'V';
	}

	InterlockedExchange(&bLock, 0);
}

char GetChar(int offset)
{
	InitKeyChars();
	if(offset >= 0 && offset < sizeof(kNewLicKeyChars))
		return kNewLicKeyChars[offset];
	return '*';
}

int GetOffset(char c, bool bNewChars = true)
{
	int size = sizeof(kNewLicKeyChars);
	InitKeyChars();

	for(int i = 0; i < size; i++)
	{
		if((bNewChars && kNewLicKeyChars[i] == c) || (!bNewChars && kOldLicKeyChars[i] == c))
		{
			return i;
		}
	}
	return -1;
}

void CUTILprod::TranslateOldKey(char  *key, char* newKeyBuf)
{
	if(!key || !newKeyBuf)
		return;

	newKeyBuf[0] = '\0';

	while(*key != '\0')
	{
		int offset = GetOffset(*key, false); //*key >= '0' && *key <= '9' ? *key - '0' : (*key - 'A') + 10;
		if(offset >= 0)
			*newKeyBuf = GetChar(offset);
		else
			*newKeyBuf = '\0';
		++newKeyBuf;
		++key;
	}
	*newKeyBuf = '\0';
}

int CUTILprod::Scramble2(unsigned long skey, char *sdata, void *data, unsigned long len)
	{
#ifdef TR_TREXE_ENABLE

	unsigned long checksum;
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
		if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
			sdata[0]=0;
			return 0;
			}
		}
#endif

	char* buf;
	unsigned long code = 0;
	unsigned long bitn = 0;
	unsigned long i, j = 0, k;

	if(len==0)
		{
		sdata[0] = '\0';
		return 0;
		}
	buf = new char[len];

	k = skey%len;
	for(i=0; i<len; i++)
		{
		RotateDwBits(skey, 8); // use entire key
		buf[i] = (char)(((char *)data)[(i+k)%len]^(skey*i));
		}
	for(i=0; i<len; i++)
		{
		code <<= 8;
		code |= buf[i]&0xff;
		bitn += 8;
		while(bitn>=5)
			{
			bitn -= 5;
			k = (code>>bitn)&0x1f;
			sdata[j++] = GetChar(k); //(char)(k<10 ? k+'0' : k+('A'-10));
			}
		}
	if(bitn)
		{
		k = (code<<(5-bitn))&0x1f;
		sdata[j++] = GetChar(k); //(char)(k<10 ? k+'0' : k+('A'-10));
		}
	sdata[j] = '\0';
	OutputDebugStringA("\r\n");
#ifdef TR_TREXE_ENABLE

//	TR_test();	// tamper the code
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
    	if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
			sdata[0]=0;
			}
		}

#endif
	delete [] buf;
	return 0;
	}

int CUTILprod::Scramble3(unsigned long skey, char *sdata, void *data, unsigned long len)
	{
#ifdef TR_TREXE_ENABLE

	unsigned long checksum;
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
		if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
			sdata[0]=0;
			return 0;
			}
		}
#endif

	char* buf;
	unsigned long code = 0;
	unsigned long bitn = 0;
	unsigned long i, j = 0, k;

	if(len==0)
		{
		sdata[0] = '\0';
		return 0;
		}
	buf = new char[len];

	k = skey%len;
	for(i=0; i<len; i++)
		{
		RotateDwBits(skey, 4); // use entire key
		buf[i] = (char)(((char *)data)[(i+k)%len]^(skey*i));
		}
	for(i=0; i<len; i++)
		{
		code <<= 8;
		code |= buf[i]&0xff;
		bitn += 8;
		while(bitn>=5)
			{
			bitn -= 5;
			k = (code>>bitn)&0x1f;
			sdata[j++] = GetChar(k); //(char)(k<10 ? k+'0' : k+('A'-10));
			}
		}
	if(bitn)
		{
		k = (code<<(5-bitn))&0x1f;
		sdata[j++] = GetChar(k); //(char)(k<10 ? k+'0' : k+('A'-10));
		}
	sdata[j] = '\0';
	OutputDebugStringA("\r\n");
#ifdef TR_TREXE_ENABLE

//	TR_test();	// tamper the code
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
    	if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
			sdata[0]=0;
			}
		}

#endif
	delete [] buf;
	return 0;
	}

int CUTILprod::Scramble(unsigned long skey, char *sdata, void *data, unsigned long len)
	{
	char buf[UTIL_PROD_LEN];
	unsigned long code = 0;
	unsigned long bitn = 0;
	unsigned long i, j = 0, k;

	if(len==0)
		{
		sdata[0] = '\0';
		return 0;
		}
	k = skey%len;
	for(i=0; i<len; i++)
		buf[i] = (char)(((char *)data)[(i+k)%len]^(skey*i));
	for(i=0; i<len; i++)
		{
		code <<= 8;
		code |= buf[i]&0xff;
		bitn += 8;
		while(bitn>=5)
			{
			bitn -= 5;
			k = (code>>bitn)&0x1f;
			sdata[j++] = (char)(k<10 ? k+'0' : k+('A'-10));
			}
		}
	if(bitn)
		{
		k = (code<<(5-bitn))&0x1f;
		sdata[j++] = (char)(k<10 ? k+'0' : k+('A'-10));
		}
	sdata[j] = '\0';
#ifdef TR_TREXE_ENABLE

	unsigned long checksum;
	
//	TR_test();	// tamper the code
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
		if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
			sdata[0]=0;
			}
		}

#endif
	return 0;
	}

int CUTILprod::Descramble(unsigned long skey, char *sdata, void *data, unsigned long len)
	{
	char buf[UTIL_PROD_LEN];
	unsigned long slen = strlen(sdata);
	unsigned long code = 0;
	unsigned long bitn = 0;
	unsigned long i, j = 0;
	unsigned long k;

#ifdef TR_TREXE_ENABLE

	unsigned long checksum;
	
//	TR_test();	// tamper the code
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
		if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
			slen=0;
			}
		}

#endif
	for(i=0; i<slen; i++)
		{
		code <<= 5;
		code |= sdata[i]<='9' ? sdata[i]-'0' : sdata[i]-('A'-10);
		bitn += 5;
		if(bitn>=8)
			{
			if(j==len)
				return -1;
			bitn -= 8;
			buf[j++] = (char)((code>>bitn)&0xff);
			}
		}
	if(j)
		{
		k = skey%j;
		for(i=0; i<j; i++)
			((char *)data)[(i+k)%j] = (char)(buf[i]^(skey*i));
		}
	if(j<len)
		((char *)data)[j] = '\0';
	return 0;
	}


int CUTILprod::Descramble2(unsigned long skey, char *sdata, void *data, unsigned long len)
	{
	char buf[1024];
	unsigned long slen = strlen(sdata);
	unsigned long code = 0;
	unsigned long bitn = 0;
	unsigned long i, j = 0;
	unsigned long k;

#ifdef TR_TREXE_ENABLE

	unsigned long checksum;
	
//	TR_test();	// tamper the code
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
		if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
    		slen=0;
			}
		}

#endif
	for(i=0; i<slen; i++)
		{
		code <<= 5;
    	code |= GetOffset(sdata[i]); //sdata[i]<='9' ? sdata[i]-'0' : sdata[i]-('A'-10);
		bitn += 5;
		if(bitn>=8)
			{
			if(j==len)
				return -1;
			bitn -= 8;
			buf[j++] = (char)((code>>bitn)&0xff);
			}
		}
	OutputDebugStringA("\r\n");
	if(j)
		{
		k = skey%j;
		for(i=0; i<j; i++)
			{
			RotateDwBits(skey, 8); // use entire key
			((char *)data)[(i+k)%j] = (char)(buf[i]^(skey*i));
			}
		}
	if(j<len)
		((char *)data)[j] = '\0';
	return 0;
	}

int CUTILprod::Descramble3(unsigned long skey, char *sdata, void *data, unsigned long len)
	{
	char buf[1024];
	unsigned long slen = strlen(sdata);
	unsigned long code = 0;
	unsigned long bitn = 0;
	unsigned long i, j = 0;
	unsigned long k;

#ifdef TR_TREXE_ENABLE

	unsigned long checksum;
	
//	TR_test();	// tamper the code
	if (TR_ChecksumEnable_9==TR_CHECKSUM_ENABLE_9)
		{		// code checksum enabled
		CTR::Checksum((void *)TR_RegionBegin_9, (void *)TR_RegionEnd_9, TR_CHECKSUM_SEED_9, &checksum, TR_RelocateTable_9);
		if ((TR_ChecksumValue_9^checksum)!=TR_CHECKSUM_VALUE_9)
			{
			// if wrong code checksum
    		slen=0;
			}
		}

#endif
	for(i=0; i<slen; i++)
		{
		code <<= 5;
    	code |= GetOffset(sdata[i]); //sdata[i]<='9' ? sdata[i]-'0' : sdata[i]-('A'-10);
		bitn += 5;
		if(bitn>=8)
			{
			if(j==len)
				return -1;
			bitn -= 8;
			buf[j++] = (char)((code>>bitn)&0xff);
			}
		}
	OutputDebugStringA("\r\n");
	if(j)
		{
		k = skey%j;
		for(i=0; i<j; i++)
			{
			RotateDwBits(skey, 4); // use entire key
			((char *)data)[(i+k)%j] = (char)(buf[i]^(skey*i));
			}
		}
	if(j<len)
		((char *)data)[j] = '\0';
	return 0;
	}

#ifdef TR_TREXE_ENABLE
// the following function is used as place marker.
int TR_RegionEnd_9() 
	{	// needs to have some meat otherwise it compresses with above.
	static int play;
	for (int i=0;i<100;i++) play^=play;
	return play;
	}
#endif //TR_TREXE_ENABLE



